Python: Clear stale service session on full-history replay - #7354
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an executor-layer session-continuation bug in the Python workflow runtime: when an AgentExecutorRequest replays a prior conversation (including tool calls), the executor must not reuse an unrelated service_session_id from a previous run, which can incorrectly combine explicit history with a provider-side continuation handle.
Changes:
- Clear
service_session_idinAgentExecutor.run()when an incomingAgentExecutorRequestreplays function-call history. - Promote the previously strict-xfailed regression test to a normal test.
- Add a boundary test ensuring a function-result-only request preserves
service_session_idfor legitimate continuation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| python/packages/core/agent_framework/_workflows/_agent_executor.py | Clears stale service_session_id on replayed function-call history before running the agent. |
| python/packages/core/tests/workflow/test_full_conversation.py | Converts the regression from xfail to pass and adds a boundary test for function-result continuation behavior. |
| if any(content.type == "function_call" for message in request.messages for content in message.contents): | ||
| self._session.service_session_id = None |
| replays function-call history, clear the service session because those messages | ||
| provide the continuation context explicitly. | ||
| """ | ||
| if any(content.type == "function_call" for message in request.messages for content in message.contents): |
There was a problem hiding this comment.
Could we avoid using the mere presence of a function_call as the full-history signal? Group-chat orchestrators broadcast each participant's response to every other agent as AgentExecutorRequest(..., should_respond=False) at _base_group_chat_orchestrator.py:433-436, and completed tool turns include their function calls. This therefore clears every receiving participant's valid service continuation after any tool-using turn, so its next turn loses server-held history and reasoning-model histories can fail stateless replay.
| replays function-call history, clear the service session because those messages | ||
| provide the continuation context explicitly. | ||
| """ | ||
| if any(content.type == "function_call" for message in request.messages for content in message.contents): |
There was a problem hiding this comment.
Should full-history handling be shared with the other explicit-message handlers? from_messages at lines 283-294 accepts the same list[Message] history but goes straight to _run_agent_and_emit, so replaying a call/result transcript through that public handler still forwards the stale service session. OpenAI then strips the inline call under previous_response_id, leaving the request attached to the wrong server conversation, so the cross-conversation state corruption this PR addresses remains reachable.
Motivation & Context
AgentExecutorRequestcan replay an explicit prior conversation, including model-issued function calls, while the target executor still carries aservice_session_idfrom an earlier run. That combines caller-supplied history with an unrelated service-side continuation handle.The executor-layer regression was already recorded as a strict xfail when #5690 fixed the OpenAI wire-format half of #3295. Running that test with
--runxfailon currentmainfails because the agent receivesresp_PREVIOUS_RUNinstead ofNone.Description & Review Guide
service_session_idbefore handling anAgentExecutorRequestthat replays model-issuedfunction_callcontent.from_responsepath continue to preserveservice_session_id.function_callcontent is the right narrow signal that distinguishes explicit replay from a function-result continuation.Validation:
.venv/bin/uv run pytest packages/core/tests/workflow/test_full_conversation.py -q— 10 passed..venv/bin/uv run poe check -P core— formatting, lint, source Pyright, and all five test type checkers passed; 3,690 tests passed, 18 skipped, and 1 unrelated test xfailed.No live provider credentials were used. The regression captures the session value at the provider boundary, and #5690 retains the separate OpenAI wire-format protection.
Related Issue
Follow-up to #3295 and #5690. The issue is already closed; this completes the executor-layer strict-xfail follow-up documented by the merged fix, so no closing keyword is used.
No open PR found for this executor behavior. #7345 covers approval replay and touches neither affected file.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.